import 'dart:convert';

import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import 'package:redis/redis.dart';
import '../models/redis_config.dart';

class RedisController extends GetxController {
  final _storage = GetStorage();
  final configs = <RedisConfig>[].obs;
  final isLoading = false.obs;
  final searchResults = <String>[].obs;

  RedisConnection? _conn;
  Command? _command;
  final Rxn<RedisConfig> currentConfig = Rxn<RedisConfig>();

  @override
  void onInit() {
    super.onInit();
    loadConfigs();
  }

  void loadConfigs() {
    final List<dynamic> savedConfigs = _storage.read('redis_configs') ?? [];
    configs.value = savedConfigs
        .map((config) => RedisConfig.fromJson(config))
        .toList();
  }

  Future<void> saveConfig(RedisConfig config) async {
    if (config.id == null) {
      configs.add(config);
    } else {
      final index = configs.indexWhere((c) => c.id == config.id);
      if (index != -1) {
        configs[index] = config;
      }
      if (currentConfig.value?.id == config.id) {
        currentConfig.value = config;
      }
    }
    configs.refresh();
    await _storage.write(
      'redis_configs',
      configs.map((c) => c.toJson()).toList(),
    );
  }

  Future<void> deleteConfig(String id) async {
    configs.removeWhere((c) => c.id == id);
    await _storage.write(
      'redis_configs',
      configs.map((c) => c.toJson()).toList(),
    );
  }

  Future<bool> connect(RedisConfig config) async {
    try {
      final connectedList = configs.where(
        (c) => c.id != config.id && c.isConnected,
      );

      for (var c in connectedList) {
        c.isConnected = false;
      }

      if (_conn != null) {
        _conn!.close();
      }

      isLoading.value = true;
      _conn = RedisConnection();

      _command = await _conn!.connect(config.host, config.port);
      if (config.password != null) {
        var obj = ["AUTH"];
        config.username != null ? obj.add(config.username!) : null;
        obj.add(config.password!);
        await _command!.send_object(obj);
      }

      // 发送 ping 检查状态
      var result = await _command!.send_object(["PING"]);
      print('Ping result: $result');
      print(result == 'PONG' ? '✅ Redis 已连接' : '❌ Redis 未连接');

      config.isConnected = result == 'PONG';
      config.isConnected = true;
      currentConfig.value = config;
      currentConfig.refresh();
      configs.refresh();
      return true;
    } catch (e) {
      Get.snackbar('连接失败', e.toString());
      print("连接失败: ${e.toString()}");
      return false;
    } finally {
      isLoading.value = false;
    }
  }

  Future<void> disconnect() async {
    if (_conn != null) {
      await _conn!.close();
      _conn = null;
      _command = null;
      if (currentConfig.value != null) {
        currentConfig.value!.isConnected = false;
        update();
      }
      currentConfig.refresh();
      configs.refresh();
      searchResults.value = [];
    }
  }

  Future<String?> get(String key) async {
    String? result;
    try {
      isLoading.value = true;
      if (_command == null) {
        throw Exception('未连接到Redis');
      }
      result = await _command!.get(key);
      print("搜索成功: ${jsonEncode(result)}");
    } catch (e) {
      print('搜索失败: ${e.toString()}');
    } finally {
      isLoading.value = false;
    }
    return result;
  }

  Future<void> searchKeys(String pattern) async {
    try {
      isLoading.value = true;
      if (_command == null) {
        throw Exception('未连接到Redis');
      }
      var results = await searchKeysByPattern(_command!, "*$pattern*");
      print("搜索成功: ${jsonEncode(results)}");
      searchResults.value = results;
    } catch (e) {
      Get.snackbar('错误', '搜索失败: ${e.toString()}');
      print('搜索失败: ${e.toString()}');
      searchResults.value = [];
    } finally {
      isLoading.value = false;
    }
  }

  Future<List<String>> searchKeysByPattern(
    Command command,
    String pattern,
  ) async {
    final List<String> matchingKeys = []; // 用于存储所有匹配的键
    int cursor = 0; // SCAN 命令的游标，初始值为 0

    try {
      // 循环执行 SCAN 命令，直到游标返回 0（表示迭代完成）
      while (true) {
        // 发送 SCAN 命令。
        // 参数依次是：游标、MATCH 关键字、模式字符串、COUNT 关键字、每次迭代的数量（可选）
        // COUNT 只是一个提示，Redis 不保证严格返回这个数量的键。
        final scanResult = await command.send_object([
          'SCAN',
          '$cursor',
          'MATCH',
          pattern,
          'COUNT',
          '1000',
        ]);

        // 解析 SCAN 命令的返回结果。
        // 结果是一个列表：[0] 是新的游标（字符串形式），[1] 是匹配键的列表。
        final nextCursorString = scanResult[0] as String;
        final keysBatch = (scanResult[1] as List).cast<String>();

        // 将当前批次匹配到的键添加到总列表中
        matchingKeys.addAll(keysBatch);

        // 更新游标为下一次迭代使用
        cursor = int.parse(nextCursorString);

        // 如果游标为 0，表示已经遍历完所有键，退出循环
        if (cursor == 0) {
          break;
        }
      }
    } catch (e) {
      print('模糊搜索时发生错误: $e');
      rethrow; // 重新抛出异常，让调用者处理
    }

    print('模糊搜索结果: $matchingKeys');
    return matchingKeys;
  }
}
